(*
 * PdP ML
 * 
 * es.2 Valutatore Espressioni Aritmetiche
 *
 * Francesco Cotto
 *)

exception SyntErrParentesi;;
exception SyntErrChar;;
exception SyntErr;;
exception MathErrDivByZero;;

let makeStrPoint i = 
  let rec makeStrPointAcc (j, acc) = 
        if j=0 then acc^"^"
      else makeStrPointAcc (j-1, acc^" ")
   in makeStrPointAcc (i, "") ;;

let printMsg (str, i, msg) =
        print_string("\n");
	print_string(str ) ;
	print_string("\n");
	print_string(makeStrPoint i);
	print_string("\n");
	print_string(msg ) ;
	print_string("\n");;

let isInSigma c = match c with 
   '0'..'9' -> (true, c)
  |'+' -> (true, c)
  |'-' -> (true, c)
  |'*' -> (true, c)
  |'/' -> (true, c)
  |'(' -> (true, c)
  |')' -> (true, c)
  |'#' -> (true, c)
  | _  -> (false, c) ;;
  
let isDigit  = function '0'..'9' -> true | _ -> false;;

let getChar (str, i) = let cc = isInSigma str.[i] in 
   if fst cc 
      then snd cc
      else (printMsg (str, i, "Errore: Carattere non in alfabeto" ) ;
            raise SyntErrChar );;


let rec rNumber(str,i, acc) = 
   if isDigit (getChar (str, i) ) 
      then let acc = (acc * 10) + int_of_char (getChar (str, i) ) - 48 
              in rNumber (str, i+1, acc) 
      else (acc,i) ;;



let expValue s =

let rec rE (str, i) = 
   let evt = rT (str, i) in 
      match getChar (str, snd evt) with 
          '+' -> let eve = rE (str, snd evt + 1 ) in 
                        (fst evt + fst eve, snd eve)
         |'-' -> let eve = rE (str, snd evt + 1 ) in 
                        (fst evt - fst eve, snd eve)
         | _  -> evt 

and

rT (str, i) = 
   let evf = rF(str, i) in
      match getChar (str, snd evf) with 
         '*' -> let evt = rT(str, snd evf+1) in
                   (fst evf * fst evt, snd evt)
        |'/' -> ( let evt = rT(str, snd evf+1) in
                    match fst evt with
                      | 0 -> printMsg (str, snd evf, "rT : Divisione per 0");
                             raise MathErrDivByZero 
                      | _ -> (fst evf / fst evt, snd evt) )
        | _  -> evf

and 


rF (str, i) =  
   if isDigit ( getChar(str, i) )
      then rNumber (str, i, 0) 
      else match getChar (str, i) with 
               '-' -> if isDigit ( getChar(str, i+1) ) 
                       then let er = rNumber (str, i+1, 0) 
                              in (fst er * -1, snd er)
                       else (printMsg (str, i+1, "rF : Carattere non atteso ");
                             raise SyntErr)
              |'(' -> ( let eve = rE(str, i+1) in 
                         match getChar (str, snd eve) with
                            ')' -> (fst eve, snd eve + 1) 
                           | _  -> printMsg (str, snd eve, "rF : Atteso ')' "); 
                                   raise SyntErrParentesi )
              | _ -> printMsg (str, i, "rF : Carattere non atteso ");
                     raise SyntErr

(* let expValue s *)
in  fst  ( rE (s ^ "#", 0) );;

let rec mainLoop() =
        print_newline ();
        print_string "expValue>"; 
        let str = read_line()
        in match str with
        "" | "quit" | "end" | "exit" -> ()
        | _ ->  ((try
                   let resVal = expValue str in
                   print_string "   ----->=" ;
                   print_int resVal;
                  with x -> ());
                  mainLoop());; 

mainLoop();;
(* per compilatore ocamlc *)

(*

(* Get the argument with Sys.argv *)
let s =  Sys.argv.(1);; 
expValue s ;;
*)


(*
(* Loop per realizzare prompt valutatore
 * da usare in windows solo per ocamlC per .exe *)
mainLoop();;
*)
